#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/ide.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/timer.h>
#include <linux/i2c.h>

#define DEV_NAME "sht30"

#define SOFTRESET 0x30A2
#define MEAS_NOSTRETCH_MEDREP 0x240B


#ifndef DEV_MAJOR
#define DEV_MAJOR       0
#endif


struct sht30_priv {
	struct cdev         cdev;
	struct class        *dev_class;
	struct i2c_client   *client;
	struct device       *dev;
};

static int dev_major = DEV_MAJOR;

static int sht30_init(struct i2c_client *client)
{
	int rv = 0;
	uint16_t data = SOFTRESET;
	unsigned char cmd[2];
	cmd[0] = data >> 8;
	cmd[1] = data;
	
	rv = i2c_master_send(client,cmd,2);
	if(rv<0)
	{
		dev_err(&client->dev,"Send i2c SOFTRESET cmd failure.\n");
		return rv;
	}

	//为啥
	msleep(50);
	return 0;
}

static int sht30_open(struct inode* inode , struct file* filp )
{
	struct sht30_priv * priv = container_of(inode->i_cdev,struct sht30_priv,cdev);
	int rv = 0;

	//Init sht30.
	rv = sht30_init(priv->client);
	if(rv < 0)
	{
		dev_err(priv->dev, "sht30 init failure.\n");
		return rv;
	}

	filp->private_data = priv;
	return 0;

}

static int read_data(struct i2c_client* client ,uint8_t * buf,size_t cnt)
{
	int rv =0;
	uint16_t data = MEAS_NOSTRETCH_MEDREP;
	unsigned char cmd[2];
	cmd[0] = data >> 8;
	cmd[1] = data;

	rv = i2c_master_send(client,cmd,2);
	if(rv < 0)
	{
		dev_err(&client->dev,"sht30 send read cmd failure.\n");
		return -EFAULT;
	}

	msleep(85);

	rv = i2c_master_recv(client,buf, cnt);
	if(rv<6)
	{
		dev_err(&client->dev,"Read data failure.\n");
		return -EFAULT;
	}
	return 0;
}
static ssize_t sht30_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	
	struct	sht30_priv * priv = filp->private_data;
	int rv = 0 ;
	uint8_t bufz[6];
	uint8_t data[4];

	if(!priv->client)
	{
		dev_err(priv->dev,"Can't get i2c client.\n");
		return -EFAULT;
	}
	
	rv = read_data(priv->client,bufz,sizeof(bufz));
	if(rv < 0 )
	{
		return -EFAULT;
	}

	data[0] = bufz[1];
	data[1] = bufz[0];
	data[2] = bufz[4];
	data[3] = bufz[3];

	rv = copy_to_user(buf, data, sizeof(data));
	if(rv)
	{
		dev_err(priv->dev, "copy to user error.\n");
		return -EFAULT;
	}

	printk("%s\n",data);

	return sizeof(data);
}

static int sht30_release(struct inode *inode, struct file *filp)
{
	    return 0;
}




static struct file_operations sht30_fops = {
	.owner = THIS_MODULE,
	.open  = sht30_open,
	.read  = sht30_read,
	.release = sht30_release,
};

static ssize_t sht30_f_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct sht30_priv *priv = dev_get_drvdata(dev);
	int rv = 0;
	uint8_t bufz[6];
	uint16_t ST, SRH;

	if(!priv->client)
	{
		dev_err(priv->dev,"Can't get i2c client.\n");
		return -EFAULT;
	}
	
	rv = read_data(priv->client,bufz,sizeof(bufz));
	if(rv < 0 )
	{
		return -EFAULT;
	}

	ST = bufz[0];
	ST <<= 8;
	ST |= bufz[1];
	SRH = bufz[3];
	SRH <<= 8;
	SRH |= bufz[4];

	return sprintf(buf,"Temperture:%d, humidity:%d.\n",ST,SRH);
}
static ssize_t sht30_f_store(struct device *dev,struct device_attribute *attr,const char *buf,size_t count)
{
	return count;
}

static DEVICE_ATTR(sht30_f, 0644, sht30_f_show, sht30_f_store);


static int sht30_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct sht30_priv *priv = NULL;
	//struct device *dev;
	dev_t devno; //设备号
	int rv = 0;

	// Allocate space for priv
	priv = devm_kzalloc(&client->dev, sizeof(struct sht30_priv), GFP_KERNEL);
	if(!priv)
	{
		return -ENOMEM;
	}

	//Create devno.
	if(0 != dev_major)
	{
		devno = MKDEV(dev_major, 0);
		rv = register_chrdev_region(devno, 1, DEV_NAME); //静态创建
	}
	else
	{
		rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//动态创建
		dev_major = MAJOR(devno);//获主设备号
	}

	if(rv < 0)
	{
		dev_err(&client->dev, "%s driver can't get major %d\n", DEV_NAME, dev_major);
		return rv;
	}

	//Register chardev.
	cdev_init(&priv->cdev, &sht30_fops); //初始化cdev
	priv->cdev.owner = THIS_MODULE;

	rv = cdev_add(&priv->cdev, devno, 1);
	if(0 != rv)
	{
		dev_err(&client->dev, "error %d add %s device failure.\n", rv, DEV_NAME);
		goto undo_major;
	}

	//Create class.
	priv->dev_class = class_create(THIS_MODULE, DEV_NAME);
	if(IS_ERR(priv->dev_class))
	{
		dev_err(&client->dev, "%s driver create class failure.\n", DEV_NAME);
		rv = -ENOMEM;
		goto undo_cdev;
	}

	//Create device.
	priv->dev = device_create(priv->dev_class, NULL, devno, NULL, DEV_NAME);
	if(IS_ERR(priv->dev))
	{
		rv = -ENOMEM;
		goto undo_class;
	}

	if(device_create_file(priv->dev, &dev_attr_sht30_f))
	{
		rv =  -EFAULT;
		goto undo_device;
	}

	//Store the priv in the dev.
	priv->client = client;
	i2c_set_clientdata(client, priv);
	dev_set_drvdata(priv->dev, priv);

	dev_info(&client->dev, "sht30 i2c driver probe okay.\n");
	return 0;

undo_device: 
	device_destroy(priv->dev_class,devno);
undo_class:
	class_destroy(priv->dev_class);

undo_cdev:
	cdev_del(&priv->cdev);

undo_major:
	unregister_chrdev_region(devno, 1);

	devm_kfree(&client->dev, priv);
	return rv;
}
static int sht30_remove(struct i2c_client *client)
{
    struct sht30_priv *priv = i2c_get_clientdata(client);
    dev_t devno = MKDEV(dev_major, 0);

	device_remove_file(priv->dev,&dev_attr_sht30_f);
    //Destroy the device.
    device_destroy(priv->dev_class, devno);

    //Destroy the class.
    class_destroy(priv->dev_class);

    //Delete the chardev.
    cdev_del(&priv->cdev);
    unregister_chrdev_region(devno, 1);

    // Release the space.
    devm_kfree(&client->dev, priv);
    dev_info(&client->dev, "sht30 driver remove.\n");
    
    return 0;
}

static const struct i2c_device_id sht30_id[] = {
	    {"wuhu,sht30", 0},
		{}
};

static const struct of_device_id of_sht30_match [] = {
	{.compatible="wuhu,sht30"},
	{}
};

static struct i2c_driver sht30_drv = {
	.probe = sht30_probe,
	.remove = sht30_remove,
	.driver = {
		.name = "hoshino-sht30", //name 不重要
		.owner = THIS_MODULE,
		.of_match_table = of_sht30_match,
	},
	.id_table = sht30_id,
};

//注册sht30驱动
module_i2c_driver(sht30_drv);
MODULE_AUTHOR("Liao Jiaqin <oroj1maru@163.com>");
MODULE_DESCRIPTION("i.MX6ULL sht30 driver");
MODULE_LICENSE("GPL");

